/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2021-05-24     18782       the first version
 */
//日志标签定义
#define LOG_TAG     "TcpClient"
#include <COMMON_HEADER/common_header.h>

#define BUFF_SIZE (1024)
#define MAX_VAL(A, B) ((A) > (B) ? (A) : (B))
#define STRCMP(a, R, b) (strcmp((a), (b)) R 0)
#define RX_CB_HANDLE(_buff, _len)  \
    do                             \
    {                              \
        if (thiz->rx)              \
            thiz->rx(_buff, _len); \
    } while (0)

#define PRINTF_TEST(_buff, _len, _tag) \
    do                                 \
    {                                  \
        _buff[_len] = '\0';            \
        LOG_D(":%s\n", _buff);         \
    } while (0)

#define EXCEPTION_HANDLE(_bytes, _tag, _info_a, _info_b)                  \
    do                                                                    \
    {                                                                     \
        if (_bytes < 0)                                                   \
        {                                                                 \
            LOG_D("return: %d\n", _bytes);                \
            goto exit;                                                    \
        }                                                                 \
        if (_bytes == 0)                                                  \
        {                                                                 \
            if (STRCMP(_info_b, ==, "warning"))                           \
                LOG_D("return: %d\n", _bytes);                            \
            else                                                          \
            {                                                             \
                LOG_D("return: %d\n", _bytes);                            \
                RX_CB_HANDLE("TCP disconnect", strlen("TCP disconnect")); \
                goto exit;                                                \
            }                                                             \
        }                                                                 \
    } while (0)

#define EXIT_HANDLE(_buff)                                            \
    do                                                                \
    {                                                                 \
        if (STRCMP(_buff, ==, "exit"))                                \
        {                                                             \
            LOG_D("exit handle, receive [exit], exit thread\n");   \
            LOG_D("exit handle, user clean up resources pleas\n"); \
            goto exit;                                                \
        }                                                             \
    } while (0)

/*tcp客户端结构体*/
struct rt_tcpclient
{
    uint8_t tcpclient_number;
    int sock_fd;
    int pipe_read_fd;
    int pipe_write_fd;
    char pipe_name[8];
    //char tcpcilent_name[50];
    rx_cb_t rx;
};

static rt_event_t tc_event = RT_NULL;
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;

#define TC_TCPCLIENT_CLOSE (1 << 0)
#define TC_EXIT_THREAD (1 << 1)
#define TC_SWITCH_TX (1 << 2)
#define TC_SWITCH_RX (1 << 3)
#define STRCMP(a, R, b) (strcmp((a), (b)) R 0)

static void rt_tc_test_deinit(rt_tcpclient_t *thiz);

static rt_tcpclient_t *tcpclient_create(uint8_t tcpcilent_number);
static rt_int32_t tcpclient_destory(rt_tcpclient_t *thiz);
static rt_int32_t socket_init(rt_tcpclient_t *thiz, const char *url, rt_uint32_t port);
static rt_int32_t socket_deinit(rt_tcpclient_t *thiz);
static rt_int32_t pipe_init(rt_tcpclient_t *thiz);
static rt_int32_t pipe_deinit(rt_tcpclient_t *thiz);
static void select_handle(rt_tcpclient_t *thiz, char *pipe_buff, char *sock_buff);
static rt_int32_t tcpclient_thread_init(rt_tcpclient_t *thiz);
static void tcpclient_thread_entry(void *param);

/**
 * @brief tcp客户端创建函数
 * @author shijiabao
 * @date 2021.08.20
 * @param tcpcilent_number：tcp客户端的编号
 * @return rt_tcpclient_t：tcp客户端结构体
 *         RT_NULL：失败
 */
/*创建tcp客户端，返回值为tcp客户端结构体*/
static rt_tcpclient_t *tcpclient_create(uint8_t tcpclient_number)
{
    rt_tcpclient_t *thiz = RT_NULL;

    thiz = rt_malloc(sizeof(rt_tcpclient_t));
    if (thiz == RT_NULL)
    {
        LOG_D("tcpclient%d alloc, malloc error\n",tcpclient_number);
        return RT_NULL;
    }

    thiz->tcpclient_number = tcpclient_number;
    thiz->sock_fd = -1;
    thiz->pipe_read_fd = -1;
    thiz->pipe_write_fd = -1;
    memset(thiz->pipe_name, 0, sizeof(thiz->pipe_name));
    thiz->rx = RT_NULL;

    return thiz;
}

/**
 * @brief tcp客户端摧毁函数
 * @author shijiabao
 * @date 2021.08.20
 * @param rt_tcpclient_t：tcp客户端结构体
 * @return 0：成功
 *        -1：失败
 */
static rt_int32_t tcpclient_destory(rt_tcpclient_t *thiz)
{
    int res = 0;

    if (thiz == RT_NULL)
    {
        LOG_D("tcpclient%d del, param is NULL, delete failed\n", thiz->tcpclient_number);
        return -1;
    }

    if (thiz->sock_fd != -1)
        socket_deinit(thiz);

    if (thiz->pipe_read_fd != -1)
    {
        res = close(thiz->pipe_read_fd);
        RT_ASSERT(res == 0);
        thiz->pipe_read_fd = -1;
    }

    if (thiz->pipe_write_fd != -1)
    {
        res = close(thiz->pipe_write_fd);
        RT_ASSERT(res == 0);
        thiz->pipe_write_fd = -1;
    }

    LOG_D("tcpclient%d del, delete succeed\n",thiz->tcpclient_number);

    free(thiz);
    thiz = RT_NULL;

    return 0;
}


/**
 * @brief tcp客户端的socket套接字初始化，以及网络连接
 * @author shijiabao
 * @date 2021.08.20
 * @param rt_tcpclient_t：tcp客户端结构体
 *        url：IP地址
 *        port：端口
 * @return 0：成功
 *        -1：失败
 */
static rt_int32_t socket_init(rt_tcpclient_t *thiz, const char *url, rt_uint32_t port)
{
    struct sockaddr_in dst_addr;
    struct hostent *hostname;
    rt_int32_t res = 0;

    if (thiz == RT_NULL)
        return -1;

    thiz->sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (thiz->sock_fd == -1)
    {
        LOG_D("tcpclient%d socket init,socket create failed\n",thiz->tcpclient_number);
        return -1;
    }

    LOG_D("tcpclient%d socket:%d\n",thiz->tcpclient_number,thiz->sock_fd);

    hostname = gethostbyname(url);

    dst_addr.sin_family = AF_INET;
    dst_addr.sin_port = htons(port);
    dst_addr.sin_addr = *((struct in_addr *)hostname->h_addr);
    memset(&(dst_addr.sin_zero), 0, sizeof(dst_addr.sin_zero));

    /*最多尝试连接5次,每次间隔5s*/
    for(int i = 0; i< 5 ; i++)
    {
        res = connect(thiz->sock_fd, (struct sockaddr *)&dst_addr, sizeof(struct sockaddr));
        if (res == -1 )
        {
            if(i < 4)
            {
                LOG_D("tcpclient%d socket init,socket connect failed\n",thiz->tcpclient_number);
            }
            else
            {
                return -1;
            }

        }
        else
        {
            break;
        }
        rt_thread_mdelay(5000);
    }
    /*为0的状态说明已经连接上*/
    dn400_parm.dn400_status.transparent_transmission_server1_connect_flag = 0;
    LOG_D("tcpclient%d socket init,TCP connected succeed\n",thiz->tcpclient_number);
    return 0;
}

/**
 * @brief tcp客户端的socket套接字关闭
 * @author shijiabao
 * @date 2021.08.20
 * @param rt_tcpclient_t：tcp客户端结构体
 * @return 0：成功
 *        -1：失败
 */
static rt_int32_t socket_deinit(rt_tcpclient_t *thiz)
{
    int res = 0;

    if (thiz == RT_NULL)
    {
        LOG_D("tcpclient%d socket deinit,param is NULL, socket deinit failed\n",thiz->tcpclient_number);
        return -1;
    }

    res = closesocket(thiz->sock_fd);
    RT_ASSERT(res == 0);

    thiz->sock_fd = -1;

    LOG_D("tcpclient%d socket deinit,socket close succeed\n",thiz->tcpclient_number);

    return 0;
}

/**
 * @brief tcp客户端的管道初始化
 * @author shijiabao
 * @date 2021.08.20
 * @param rt_tcpclient_t：tcp客户端结构体
 * @return 0：成功
 *        -1：失败
 */
static rt_int32_t pipe_init(rt_tcpclient_t *thiz)
{
    char dev_name[32];
    static int pipeno = 0;
    rt_pipe_t *pipe = RT_NULL;

    if (thiz == RT_NULL)
    {
        LOG_D("tcpclient%d pipe init, param is NULL\n",thiz->tcpclient_number);
        return -1;
    }

    snprintf(thiz->pipe_name, sizeof(thiz->pipe_name), "pipe%d", pipeno++);

    pipe = rt_pipe_create(thiz->pipe_name, PIPE_BUFSZ);
    if (pipe == RT_NULL)
    {
        LOG_D("tcpclient%d pipe create, pipe create failed\n",thiz->tcpclient_number);
        return -1;
    }

    snprintf(dev_name, sizeof(dev_name), "/dev/%s", thiz->pipe_name);
    thiz->pipe_read_fd = open(dev_name, O_RDONLY, 0);
    if (thiz->pipe_read_fd < 0)
        goto fail_read;

    thiz->pipe_write_fd = open(dev_name, O_WRONLY, 0);
    if (thiz->pipe_write_fd < 0)
        goto fail_write;

    LOG_D("tcpclient%d pipe init, pipe init succeed\n",thiz->tcpclient_number);
    return 0;

fail_write:
    close(thiz->pipe_read_fd);
fail_read:
    return -1;
}

/**
 * @brief tcp客户端的管道摧毁
 * @author shijiabao
 * @date 2021.08.20
 * @param rt_tcpclient_t：tcp客户端结构体
 * @return 0：成功
 *        -1：失败
 */
static rt_int32_t pipe_deinit(rt_tcpclient_t *thiz)
{
    int res = 0;

    if (thiz == RT_NULL)
    {
        LOG_D("tcpclient%d pipe deinit, param is NULL, pipe deinit failed\n", thiz->tcpclient_number);
        return -1;
    }

    res = close(thiz->pipe_read_fd);
    RT_ASSERT(res == 0);
    thiz->pipe_read_fd = -1;

    res = close(thiz->pipe_write_fd);
    RT_ASSERT(res == 0);
    thiz->pipe_write_fd = -1;

    rt_pipe_delete(thiz->pipe_name);

    LOG_D("tcpclient%d pipe deinit, pipe close succeed\n", thiz->tcpclient_number);
    return 0;
}

//sock接收数据缓存区
char sock_recv_data_buff[1048];

/**
 * @brief select处理函数，通过检查pipe和socket来进行收发数据
 * @author shijiabao
 * @date 2021.08.20
 * @param thiz：客户端结构体
 *        pipe_buff：管道地址
 *        sock_buff：套接字地址
 * @return
 */
static void select_handle(rt_tcpclient_t *thiz, char *pipe_buff, char *sock_buff)
{
    fd_set fds;

    rt_int32_t max_fd = 0, res = 0;
    max_fd = MAX_VAL(thiz->sock_fd, thiz->pipe_read_fd) + 1;

    /* 清空可读事件描述符列表 */
    FD_ZERO(&fds);

    while (1)
    {
        /* 将需要监听可读事件的描述符加入列表 */
        FD_SET(thiz->sock_fd, &fds);
        FD_SET(thiz->pipe_read_fd, &fds);

        /* 等待设定的网络描述符有事件发生 */
        res = select(max_fd, &fds, RT_NULL, RT_NULL, RT_NULL);

        /* select 返回错误及超时处理 */
        EXCEPTION_HANDLE(res, "select handle", "error", "timeout");

         /* 查看 sock 描述符上有没有发生可读事件 */
        if (FD_ISSET(thiz->sock_fd, &fds))
        {
            /* 从 sock 连接中接收最大BUFSZ - 1字节数据 */
            res = recv(thiz->sock_fd, sock_buff, BUFF_SIZE, 0);

            /* recv 返回异常 */
            EXCEPTION_HANDLE(res, "socket recv handle", "error", "TCP disconnected");

            /* 有接收到数据，把末端清零 */
            sock_buff[res] = '\0';

            /* 通过回调函数的方式，数据发给 thread1 */
            RX_CB_HANDLE(sock_buff, res);

            /* 如果接收的是exit，关闭这个连接 */
            EXIT_HANDLE(sock_buff);
        }

        /* 查看 pipe 描述符上有没有发生可读事件 */
        if (FD_ISSET(thiz->pipe_read_fd, &fds))
        {
            /* 从 pipe 连接中接收最大BUFSZ - 1字节数据 */
            res = read(thiz->pipe_read_fd, pipe_buff, BUFF_SIZE);

            /* recv 返回异常 */
            EXCEPTION_HANDLE(res, "pipe recv handle", "error", RT_NULL);

            /* 有接收到数据，把末端清零 */
            pipe_buff[res] = '\0';

            /* 读取 pipe 的数据，转发给 server */
            send(thiz->sock_fd, pipe_buff, res, 0);

            /* recv 返回异常 */
            EXCEPTION_HANDLE(res, "socket write handle", "error", "warning");

            /* 如果接收的是 exit，关闭这个连接 */
            EXIT_HANDLE(pipe_buff);
        }
    }
exit:
    /* 释放接收缓冲 */
    free(pipe_buff);
    free(sock_buff);
}

/**
 * @brief tcp客户端线程初始化
 * @author shijiabao
 * @date 2021.08.20
 * @param
 * @return
 */
static rt_int32_t tcpclient_thread_init(rt_tcpclient_t *thiz)
{
    rt_thread_t tcpclient_tid = RT_NULL;

    tcpclient_tid = rt_thread_create("tcpc", tcpclient_thread_entry, thiz, 2048, 12, 10);
    if (tcpclient_tid == RT_NULL)
    {
        LOG_D("tcpclient%d tcpclient thread, thread create failed\n",thiz->tcpclient_number);
        return -1;
    }

    rt_thread_startup(tcpclient_tid);

    LOG_D("tcpclient%d tcpclient thread, thread init succeed\n",thiz->tcpclient_number);
    return 0;
}

/**
 * @brief tcp客户端线程
 * @author shijiabao
 * @date 2021.08.20
 * @param
 * @return
 */
static void tcpclient_thread_entry(void *param)
{
    rt_tcpclient_t *temp = param;
    char *pipe_buff = RT_NULL, *sock_buff = RT_NULL;

    pipe_buff = malloc(BUFF_SIZE);
    if (pipe_buff == RT_NULL)
    {
        LOG_D("tcpclient%d thread entry, malloc error\n",temp->tcpclient_number);
        LOG_D("tcpclient%d thread entry, exit\n",temp->tcpclient_number);
        return;
    }

    sock_buff = malloc(BUFF_SIZE);
    if (sock_buff == RT_NULL)
    {
        free(pipe_buff);
        LOG_D("tcpclient%d thread entry, malloc error\n",temp->tcpclient_number);
        LOG_D("tcpclient%d thread entry, exit\n",temp->tcpclient_number);
        return;
    }

    memset(sock_buff, 0, BUFF_SIZE);
    memset(pipe_buff, 0, BUFF_SIZE);

    select_handle(temp, pipe_buff, sock_buff);
}

rt_tcpclient_t *rt_tcpclient_start(uint8_t tcpclient_number,const char *hostname, rt_uint32_t port)
{
    rt_tcpclient_t *thiz = RT_NULL;

    thiz = tcpclient_create(tcpclient_number);
    if (thiz == RT_NULL)
        return RT_NULL;

    if (socket_init(thiz, hostname, port) != 0)
        goto quit;

    if (pipe_init(thiz) != 0)
        goto quit;

    if (tcpclient_thread_init(thiz) != 0)
        goto quit;

    LOG_D("tcpclient%d start, tcpclient start succeed\n",thiz->tcpclient_number);
    return thiz;

quit:
    tcpclient_destory(thiz);
    return RT_NULL;
}

void rt_tcpclient_close(rt_tcpclient_t *thiz)
{
    if (thiz == RT_NULL)
    {
        LOG_D("tcpclient%d deinit, param is NULL, tcpclient deinit failed\n", thiz->tcpclient_number);
        return;
    }

    if (socket_deinit(thiz) != 0)
        return;

    if (pipe_deinit(thiz) != 0)
        return;

    if (tcpclient_destory(thiz) != 0)
        return;

    LOG_D("tcpclient%d tcpclient deinit, tcpclient deinit succeed\n",thiz->tcpclient_number);
}

rt_int32_t rt_tcpclient_send(rt_tcpclient_t *thiz, const void *buff, rt_size_t len)
{
    rt_size_t bytes = 0;

    if (thiz == RT_NULL)
    {
        LOG_D("tcpclient%d send, param is NULL\n", thiz->tcpclient_number);
        return -1;
    }

    if (buff == RT_NULL)
    {
        LOG_D("tcpclient%d send, buff is NULL\n",thiz->tcpclient_number);
        return -1;
    }

    bytes = write(thiz->pipe_write_fd, buff, len);
    return bytes;
}

rt_int32_t rt_tcpclient_attach_rx_cb(rt_tcpclient_t *thiz, rx_cb_t cb)
{
    if (thiz == RT_NULL)
    {
        LOG_D("%s callback attach, param is NULL\n",thiz->tcpclient_number);
        return -1;
    }

    thiz->rx = cb;
    LOG_D("%s callback attach, attach succeed\n",thiz->tcpclient_number);
    return 0;
}

/**
 * @brief 创建套接字
 * @author shijiabao
 * @date 2021.08.19 rewrite
 * @param
 * @return
 */
static rt_int32_t socket_create(char *work_mode)
{
    int sock_fd = -1;
    //UDP连接模式
    if (strstr(work_mode, "UDP") != NULL)
    {
        sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
    }
    //TCP连接模式
    else
    {
        sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    }

    //判断套接字是否有效
    if (sock_fd == -1)
    {
        LOG_D("create socket failed!");
        return -1;
    }

    //返回套接字
    return sock_fd;
}

/**
 * @brief 建立socket连接
 * @author shijiabao
 * @date 2021.08.19 rewrite
 * @param
 * @return
 */
rt_int32_t socket_connect(int sock_fd, const char *url, rt_uint32_t port)
{
    struct sockaddr_in dst_addr;
    struct hostent *hostname;
    rt_int32_t res = 0;
    hostname = gethostbyname(url);

    dst_addr.sin_family = AF_INET;
    dst_addr.sin_port = htons(port);
    dst_addr.sin_addr = *((struct in_addr *) hostname->h_addr);
    memset(&(dst_addr.sin_zero), 0, sizeof(dst_addr.sin_zero));

    res = connect(sock_fd, (struct sockaddr *) &dst_addr, sizeof(struct sockaddr));

    //连接失败
    if (res == -1)
        return -1;
    //
    return 0;
}

/**
 * @brief 发送tcp数据
 * @author shijiabao
 * @date 2021.08.19 rewrite
 * @param
 * @return
 */
rt_int32_t tcpclient_send_data(int sock_fd, char *send_buff, rt_size_t len)
{
    rt_size_t bytes = 0;
    bytes = write(sock_fd, send_buff, len);
    return bytes;
}

/**
 * @brief 接收tcp数据
 * @author shijiabao
 * @date 2021.08.19 rewrite
 * @param
 * @return
 */
rt_int32_t tcpclient_recv_data(int sock_fd, char *recv_buff, rt_size_t len)
{
    rt_size_t bytes = 0;
    bytes = recv(sock_fd, recv_buff, len, 0);
    return bytes;
}

static void rt_tc_rx_cb(void *buff, rt_size_t len)
{
    char *recv = RT_NULL;

    recv = malloc(len + 1);
    if (recv == RT_NULL)
        return;

    memcpy(recv, buff, len);
    *(recv + len) = '\0';

    LOG_D("tc_rx_cb, recv data: %s\n", recv);
    LOG_D("tc_rx_cb, recv len: %d\n", strlen(recv));

    if (STRCMP(recv, ==, "exit"))
    {
        printf("receive [exit]\n");
        rt_event_send(tc_event, TC_TCPCLIENT_CLOSE);
    }

    if (STRCMP(recv, ==, "TCP disconnect"))
    {
        printf("[TCP disconnect]\n");
        rt_event_send(tc_event, TC_TCPCLIENT_CLOSE);
    }

    free(recv);
}

static void rt_tc_thread1_entry(void *param)
{
    rt_tcpclient_t *temp = param;
    const char *str = "this is thread1\r\n";
    rt_uint32_t e = 0;

    while (1)
    {
        if (rt_event_recv(tc_event, TC_TCPCLIENT_CLOSE,
                          RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
                          0, &e) == RT_EOK)
        {
            rt_event_send(tc_event, TC_EXIT_THREAD);
            rt_tc_test_deinit((rt_tcpclient_t *)param);
            return;
        }
        rt_thread_mdelay(5000);
        rt_tcpclient_send(temp, str, strlen(str));
    }
}

static void rt_tc_thread2_entry(void *param)
{
    rt_tcpclient_t *temp = param;
    const char *str = "this is thread2\r\n";
    rt_uint32_t e = 0;

    while (1)
    {
        if (rt_event_recv(tc_event, TC_EXIT_THREAD,
                          RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
                          0, &e) == RT_EOK)
        {
            rt_event_delete(tc_event);
            return;
        }

        rt_thread_mdelay(5000);
        rt_tcpclient_send(temp, str, strlen(str));
    }
}

static void rt_tc_test_deinit(rt_tcpclient_t *thiz)
{
    rt_tcpclient_close(thiz);
}

int rt_tc_test_init(uint8_t tcpclient_number,const char *hostname, rt_uint32_t port)
{
    rt_tcpclient_t *handle = RT_NULL;

    /*when connect_flag is 0,tcpclient started but not connect*/
    if(tcpclient_number == 1)
    {
        dn400_parm.dn400_status.transparent_transmission_server1_connect_flag = 0;
    }
    else if(tcpclient_number == 2)
    {
        dn400_parm.dn400_status.transparent_transmission_server2_connect_flag = 0;
    }
    else
    {
        dn400_parm.dn400_status.private_server_connect_flag = 0;
    }

    /* 服务器的 ip 地址 & 服务器监听的端口号 */
    handle = rt_tcpclient_start(tcpclient_number,hostname, port);
    if (handle == RT_NULL)
    {
        LOG_D("tcpclient%d thread, param is NULL, exit\n", tcpclient_number);

        /* tcp client started failed,connect flag is -1 */
        if(tcpclient_number == 1)
       {
          dn400_parm.dn400_status.transparent_transmission_server1_connect_flag = -1;
       }
       else if(tcpclient_number == 2)
       {
          dn400_parm.dn400_status.transparent_transmission_server2_connect_flag = -1;
       }
       else
       {
          dn400_parm.dn400_status.private_server_connect_flag = -1;
       }
        return -1;
    }

    /* 注册接收回调函数 */
    rt_tcpclient_attach_rx_cb(handle, rt_tc_rx_cb);

    tc_event = rt_event_create("tcev", RT_IPC_FLAG_FIFO);
    if (tc_event == RT_NULL)
    {
        LOG_D("tcpclient%d event, event create failed\n", tcpclient_number);
        return -1;
    }

    tid1 = rt_thread_create("tcth1", rt_tc_thread1_entry, handle, 1024, 10, 10);
    if (tid1 == RT_NULL)
    {
        LOG_D("tcpclient%d thread1, thread1 init failed\n", tcpclient_number);
        return -1;
    }
    rt_thread_startup(tid1);

    tid2 = rt_thread_create("tcth2", rt_tc_thread2_entry, handle, 1024, 10, 10);
    if (tid2 == RT_NULL)
    {
        LOG_D("tcpclient%d thread2, thread2 init failed\n", tcpclient_number);
        return -1;
    }
    rt_thread_startup(tid2);

    return 0;
}
